#!/bin/bash
#
# Generate random xkcd-style passwords using the built-in UNIX words file!
#
# Or... generate passwords that are closer to the Diceware method.
#
# If you don't know what a xkcd-style password is, refer to the comic below:
#
#   https://xkcd.com/936/
#
# USAGE: xkcd-passgen [-d] [-l LENGTH in words]
#
# Copyright 2016 Klaus Zimmermann - https://quitter.se/kzimmermann
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#

# How many words will your password be? (We don't recommend lower than 4)
LENGTH=4

# Do you want to do Diceware instead (short words, not longer than 6 chars)?
# Default is no, override with -d
DICEWARE=false

# This is the standard words file in Debian, might be different in your 
# distribution. Change this accordingly.
WORDS=/etc/dictionaries-common/words

if [ ! -f "$WORDS" ]
then
    echo "No suitable word file found at $WORDS."
    echo "Please use another file if your distribution has one."
    exit 1
fi

helper() {
    cat <<EOF
USAGE: $(basename $0) [-d] [-l LENGTH]
Where:
    -d: enables Diceware method to be used instead
    -l: generates a passphrase LENGTH words long

This script is Free Software licensed under the GPL v3
EOF
}

while [ -n "$1" ]
do
    if [[ "$1" == "-l" ]]
    then
        LENGTH="$2"
        shift
    elif [[ "$1" == "-h" ]]
    then
        helper
        exit 0
    elif [[ "$1" == "-d" ]]
    then
        DICEWARE=true
    else
        echo "Unrecognized option '$1'"
        exit 1
    fi
    shift
done

if [[ "$DICEWARE" == true ]]
then
    echo "Diceware mode enabled."
    if [[ "$LENGTH" -le 4 ]]
    then
        LENGTH=6 # minimum since words are shorter
    fi
    cat "$WORDS" | 
        grep -e "^......$" | # only words up to 6 chars allowed.
        shuf | 
        tail -"$LENGTH" | 
        tr "\n" " " |       # now you have words all in one line 
        tr [A-Z] [a-z] |    # put everything lowercase
        sed "s/'s//g"       # remove 's that occasionally appear in the file.
else
    # "normal", unfiltered xkcd-style passgen
    cat "$WORDS" | 
        shuf | 
        tail -"$LENGTH" | 
        tr "\n" " " |       # now you have words all in one line 
        tr [A-Z] [a-z] |    # put everything lowercase
        sed "s/'s//g"       # remove 's that occasionally appear in the file.
fi


echo ""
